package com.heima.article.service.Imp;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.article.mapper.ApArticleMapper;
import com.heima.article.service.HotArticleService;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojo.AdChannel;
import com.heima.model.article.pojo.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.constants.article.ArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.mess.app.AggBehaviorDTO;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.text.DateFormat;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    AdminFeign adminFeign;
    @Autowired
    StringRedisTemplate redisTemplate;
    @Autowired
    ApArticleMapper apArticleMapper;

    /**
     * 热点文章计算
     */
    @Override
    public void computeHotArticle() {
        //1.查询五天的文章数据 转换成字符串
        String beginDate = LocalDateTime.now().minusDays(5).format(DateTimeFormatter.ofPattern("yyyy-MM-dd 0:0:0"));
        //查询五天内未删除 未下架的文章
        List<ApArticle> apArticleList = apArticleMapper.selectArticleByDate(beginDate);
        //2.计算每篇文章的分值
        List<HotArticleVo> hotArticleVoList = computeArticleScore(apArticleList);
        if (!CollectionUtils.isNotEmpty(hotArticleVoList)) {
            log.info("我们的项目太冷清了，五天都没人发文章!");
        }
        //3.缓存每个频道30条热门文章
        cacheTagToRedis(hotArticleVoList);
    }

    /**
     * 3.缓存热点文章
     *
     * @param hotArticleVoList
     */
    private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
        //1.使用feign查询频道列表
        ResponseResult<List<AdChannel>> responseResult = adminFeign.selectChannels();
        //对远程调用进行校验
        if (!responseResult.checkCode()) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW, "远程调用出错!!");
        }
        List<AdChannel> channelList = responseResult.getData();
        //2.遍历频道列表
        //找出每个频道对应的文章 进行缓存
        for (AdChannel adChannel : channelList) {
            List<HotArticleVo> articleVoList = hotArticleVoList.stream().filter(hotArticleVo -> hotArticleVo.getChannelId()
                            .equals(adChannel.getId()))
                    .collect(Collectors.toList());
            //对文章进行排序及缓存 参数：文章集合 对应的key值
            sortAndCash(articleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + adChannel.getId());
        }
    }


    /**
     * 3.1对文章进行排序及缓存 参数：文章集合 对应的key值
     *
     * @param hotArticleVos
     * @param key
     */
    private void sortAndCash(List<HotArticleVo> hotArticleVos, String key) {
        hotArticleVos = hotArticleVos.stream()
                //对单个频道的集合进行分数排序 获取分数的关键在于转换的实体类的调用
                .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                //获取前三十
                .limit(30)
                .collect(Collectors.toList());
        //进行redis缓存热点文章
        redisTemplate.opsForValue().set(key, JSON.toJSONString(hotArticleVos));
    }


    /**
     * 2. 将五天内查询到的文章进行算分
     *
     * @param apArticleList
     * @return
     */
    private List<HotArticleVo> computeArticleScore(List<ApArticle> apArticleList) {
        return apArticleList.stream()
                .map(apArticle -> { //获取集合中的apArticle
                    HotArticleVo hotArticleVo = new HotArticleVo(); //将集合中的内容进行转换
                    BeanUtils.copyProperties(apArticle, hotArticleVo);
                    //计算每篇文章的分值
                    Integer score = computeScore(apArticle);
                    hotArticleVo.setScore(score);
                    return hotArticleVo;
                }).collect(Collectors.toList());
    }

    /**
     * 2.1热点文章分值计算
     *
     * @param apArticle
     * @return
     */
    private Integer computeScore(ApArticle apArticle) {
        Integer score = 0;
        //getLikes
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        return score;
    }

    /**
     * 更新数据
     *
     * @param aggBehavior
     */
    @Override
    public void updateApArticle(AggBehaviorDTO aggBehavior) {
        //对参数进行判空
        if (aggBehavior == null) {
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "数据不能为空!");
        }
        //根据文章id获取对应的文章参数
        ApArticle apArticle = apArticleMapper.selectById(aggBehavior.getArticleId());
        //like
        if (aggBehavior.getLike() != 0) {
            int like = (int) (apArticle.getLikes() != null ? aggBehavior.getLike() : aggBehavior.getLike() + apArticle.getLikes());
            apArticle.setLikes(like);
        }
        //comment
        if (aggBehavior.getComment() != 0) {
            int comment = (int) (apArticle.getComment() != null ? aggBehavior.getComment() : aggBehavior.getComment() + apArticle.getComment());
            apArticle.setComment(comment);
        }
        //view
        if (aggBehavior.getView() != 0) {
            int view = (int) (apArticle.getViews() != null ? aggBehavior.getView() : aggBehavior.getView() + apArticle.getViews());
            apArticle.setLikes(view);
        }
        //collect
        if (aggBehavior.getCollect() != 0) {
            int collect = (int) (apArticle.getCollection() != null ? aggBehavior.getCollect() : aggBehavior.getCollect() + apArticle.getCollection());
            apArticle.setLikes(collect);
        }
        apArticleMapper.updateById(apArticle);
        //计算文章分值
        Integer score = computeScore(apArticle);
        //如果文章是今天发表的话 热度*3
        String now = DateUtils.dateToString(new Date());
        String publishTime = DateUtils.dateToString(apArticle.getPublishTime());
        if (publishTime.equals(now)) {
            score = score * 3;
        }
        //更新缓存
        updateArticleCache(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + apArticle.getChannelId());
        updateArticleCache(apArticle, score, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);


    }

    /**
     * 对redis的文章进行缓存
     *
     * @param apArticle
     * @param score
     * @param
     */
    private void updateArticleCache(ApArticle apArticle, Integer score, String cacheKey) {
        boolean flag = false;
        String hotArticleListJson = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isNotBlank(hotArticleListJson)) {
            List<HotArticleVo> hotArticleList = JSONArray.parseArray(hotArticleListJson,HotArticleVo.class);
            //1 如果当前缓存中有当前文章，更新分值
            for (HotArticleVo hotArticleVo : hotArticleList) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            //2 缓存中没有当前文章
            if (!flag) {
                HotArticleVo hotArticle = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, hotArticle);
                hotArticle.setScore(score);
                hotArticleList.add(hotArticle);
            }
            //3. 将热点文章集合 按得分降序排序  取前30条缓存至redis中
            hotArticleList = hotArticleList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleList));
        }
    }
}
